//
// Created by zzh on 2020/12/15.
//

#include "cstring"
#include "HaoFFmpeg.h"


HaoFFmpeg::HaoFFmpeg(const char *dataSource, JavaCallback *javaCallback) {
    //保存地址为全局变量，不能直接赋值
    this->dataSource = new char(strlen(dataSource));
    strcpy(this->dataSource, dataSource);
    this->javaCallback = javaCallback;
}

HaoFFmpeg::~HaoFFmpeg() {
    DELETE(dataSource);
    DELETE(javaCallback);
}

//打开视屏地址线程
void *prepare_thread(void *args) {
    HaoFFmpeg *ffmpeg = static_cast<HaoFFmpeg *>(args);
    ffmpeg->prepare_open();
    return nullptr;
}


void HaoFFmpeg::prepare() {
    pthread_create(&pid, nullptr, prepare_thread, this); //创建一个线程来启动ffmpeg
}

void HaoFFmpeg::prepare_open() {
    //1.初始化网络,让ffmpeg能使用网络
    avformat_network_init();
    //2.打开视屏地址，返回视屏信息给 AVFormatContext
    avFormatContext = nullptr;
    int ret = avformat_open_input(&avFormatContext, dataSource, nullptr, nullptr);
    if (ret != 0) { //加载视屏失败，回调给Java
        LOGE("打开媒体失败：%s", av_err2str(ret));
        javaCallback->onError(THREAD_CHILD, FFMPEG_CAN_NOT_OPEN_URL);
        return;
    }
    //3.查找媒体中的 音视频流 (给 contxt里的 streams等成员赋)
    ret = avformat_find_stream_info(avFormatContext, 0);
    if (ret < 0) { //查找信息失败
        javaCallback->onError(THREAD_CHILD, FFMPEG_CAN_NOT_FIND_STREAMS);
        return;
    }
    //4. 获取avFormatContext 的流信息，(视屏流、音频流等) ，avFormatContext->nb_streams代表包含多少个流
    for (int i = 0; i < avFormatContext->nb_streams; ++i) {
        // 此流可能代表是一个视频 也可能代表是一个音频
        AVStream *avStream = avFormatContext->streams[i];
        //包含了 解码 这段流 的各种参数信息(宽、高、码率、帧率)
        AVCodecParameters *param = avStream->codecpar;
        //查找这段流的解码器，如果没有解码器则返回null
        AVCodec *avCodec = avcodec_find_decoder(param->codec_id);
        if (avCodec == NULL) {
            LOGE("查找解码器失败:%s", av_err2str(ret));
            javaCallback->onError(THREAD_CHILD, FFMPEG_FIND_DECODER_FAIL);
            return;
        }
        //5. 获得解码器上下文
        AVCodecContext *codecContext = avcodec_alloc_context3(avCodec);
        if (codecContext == NULL) {
            LOGE("创建解码上下文失败:%s", av_err2str(ret));
            javaCallback->onError(THREAD_CHILD, FFMPEG_ALLOC_CODEC_CONTEXT_FAIL);
            return;
        }
        //6. 将流参数信息设置给解码器上下文
        ret = avcodec_parameters_to_context(codecContext, param);
        if (ret < 0) {
            LOGE("设置解码上下文参数失败:%s", av_err2str(ret));
            javaCallback->onError(THREAD_CHILD, FFMPEG_CODEC_CONTEXT_PARAMETERS_FAIL);
            return;
        }

        //7. 打开解码器
        avcodec_open2(codecContext, avCodec, 0);
        if (ret != 0) {
            LOGE("打开解码器失败:%s", av_err2str(ret));
            javaCallback->onError(THREAD_CHILD, FFMPEG_OPEN_DECODER_FAIL);
            return;
        }
        if (param->codec_type == AVMEDIA_TYPE_AUDIO) { //音频流处理
            audioChannel = new AudioChannel();
        } else if (param->codec_type == AVMEDIA_TYPE_VIDEO) {//视屏频流处理
            videoChannel = new VideoChannel();
        }
    }
    //没有音视频
    if (!audioChannel && !videoChannel) {
        LOGE("没有音视频");
        javaCallback->onError(THREAD_CHILD, FFMPEG_NOMEDIA);
        return;
    }
    // 准备完了 通知java 你随时可以开始播放
    javaCallback->onPrepare(THREAD_CHILD);
}



